<?php

define('DAO_DIR', DOMAIN_DIR.'/dao');
define('ENTITY_DIR', DOMAIN_DIR.'/entity');

function _get_value_from_description_file($entity_name, $key = null, $default = null)
{/*{{{*/
    $entity_file_path = DESCRIPTION_DIR.'/'.$entity_name.'.yml';

    otherwise(is_file($entity_file_path), "实体 $entity_name 描述文件没找到");

    $description = yaml_parse_file(DESCRIPTION_DIR.'/'.$entity_name.'.yml');

    if (is_null($key)) {
        return $description;
    }

    return array_get($description, $key, $default);
}/*}}}*/

function _generate_entity_file($entity_name, $entity_structs, $entity_relationships, $entity_options = [])
{/*{{{*/
    $content = '<?php

class %s extends entity
{
    public $structs = [
        %s
    ];

    public static $entity_display_name = \'%s\';
    public static $entity_description = \'%s\';

    public static $struct_types = [
        %s
    ];

    public static $struct_display_names = [
        %s
    ];

    public static $struct_descriptions = [
        %s
    ];
%s
    public static $struct_formats = [
        %s
    ];

    public static $struct_format_descriptions = [
        %s
    ];

    public function __construct()
    {/*{{{*/
        %s
    }/*}}}*/

    public static function create()
    {/*{{{*/
        return parent::init();
    }/*}}}*/
%s
}';

    $structs_str = [];
    $types_str = [];
    $display_names_str = [];
    $descriptions_str = [];
    $formats_str = [];
    $format_descriptions_str = [];

    $property_block = [];
    $function_block = [];

    foreach ($entity_structs as $struct) {

        $struct_name = $struct['name'];
        $struct_format = $struct['format'];
        $struct_display_name = $struct['display_name'];
        $struct_description = $struct['description'];

        // generate structs
        $structs_str[] = "'$struct_name' => '',";

        // generate struct_types
        $struct_type = 'text';
        $maps = [
            'varchar' => 'text',
            'text' => 'text',
            'int' => 'number',
            'bigint' => 'number',
            'enum' => 'enum',
        ];

        foreach ($maps as $pattern => $type) {
            if (is_array($struct_format)) {
                $struct_type = 'enum';
                break;
            } else if (stristr($struct['datatype'], $pattern)) {
                $struct_type = $type;
                break;
            }
        }

        $types_str[] = "'$struct_name' => '$struct_type',";

        // generate display_names
        $display_names_str[] =  "'$struct_name' => '$struct_display_name',";

        // generate descriptions
        $descriptions_str[] =  "'$struct_name' => '$struct_description',";

        // generate struct_formats
        if (! is_null($struct_format)) {
            if (is_array($struct_format)) {

                // generate const and map
                $const_str = [];
                $map_str = ["    const ".strtoupper($struct_name)."_MAPS = ["];

                foreach ($struct_format as $value => $description) {
                    $const_name = strtoupper($struct_name.'_'.$value);
                    $const_str[] = sprintf("    const %s = '%s';", $const_name, strtoupper($value));
                    $map_str[] = sprintf("        self::%s => '%s',", $const_name, $description);
                }

                $map_str[] = '    ];';

                $property_block[] = implode("\n", $const_str);
                $property_block[] = implode("\n", $map_str);

                $function_block[] = sprintf(
                    "    public function get_%s_description()\n".
                    "    {\n".
                    "        return self::%s[\$this->%s];\n".
                    "    }",
                    $struct_name, strtoupper($struct_name)."_MAPS", $struct_name);

                $formats_str[] = "'$struct_name' => self::".strtoupper($struct_name)."_MAPS,";

            } else {
                $formats_str[] = "'$struct_name' => '$struct_format',";
            }
        }

        $format_description = $struct['format_description'];
        if (! is_null($struct_format)) {
            $format_descriptions_str[] = "'$struct_name' => '$format_description',";
        }
    }

    $property_str = $property_block? "\n".implode("\n\n", $property_block)."\n": '';
    $function_str = $function_block? "\n".implode("\n\n", $function_block)."\n": '';

    $relationship_str = [];
    foreach ($entity_relationships as $relationship) {
        if ($relationship['relation_name'] === $relationship['relate_to']) {
            $relationship_str[] = "\$this->{$relationship['type']}('{$relationship['relate_to']}');";
        } else {
            $relationship_str[] = "\$this->{$relationship['type']}('{$relationship['relation_name']}', '{$relationship['relate_to']}', '{$relationship['relation_name']}_id');";
        }
    }

    return sprintf($content, $entity_name, implode("\n        ", $structs_str), implode("\n        ", $relationship_str));
}/*}}}*/

function _generate_dao_file($entity_name, $entity_structs, $entity_relationships)
{/*{{{*/
    return "<?php

class {$entity_name}_dao extends dao
{
    protected \$table_name = '{$entity_name}';
    protected \$db_config_key = '".unit_of_work_db_config_key()."';
}";
}/*}}}*/

function _generate_migration_file($entity_name, $entity_structs, $entity_relationships)
{/*{{{*/
    $content = "# up
        CREATE TABLE `%s` (
            `id` bigint(20) NOT NULL,
            `version` int(11) NOT NULL,
            `create_time` datetime DEFAULT NULL,
            `update_time` datetime DEFAULT NULL,
            `delete_time` datetime DEFAULT NULL,
            %s
            PRIMARY KEY (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    # down
    drop table `%s`;";

    $columns = [];

    foreach ($entity_structs as $struct) {
        $column = sprintf('`%s` %s', $struct['name'], $struct['datatype']);
        if (! $struct['allow_null']) {
            $column .= ' NOT NULL';
        }
        if ($default = $struct['default']) {
            if (is_string($default)) {
                $column .= " DEFAULT '$default'";
            } else {
                $column .= " DEFAULT $default";
            }
        } else {
            $column .= ' DEFAULT NULL';
        }

        $columns[] = $column.',';
    }

    $indexs = [];
    foreach ($entity_relationships as $relationship) {
        if ($relationship['type'] === 'belongs_to') {
            $columns[] = "`{$relationship['relate_to']}_id` bigint(20) NOT NULL,";
            $indexs[] = "KEY `fk_{$entity_name}_{$relationship['relate_to']}_idx` (`{$relationship['relation_name']}_id`),";
        }
    }

    return sprintf($content, $entity_name, implode("\n    ", array_merge($columns, $indexs)), $entity_name);
}/*}}}*/

command('entity:make', '初始化 entity、dao、migration', function ()
{/*{{{*/
    $entity_name = command_paramater('entity_name');

    $entity_structs = [];

    $s = 0;

    while (command_read_bool('Add struct')) {

        $s += 1;

        $entity_structs[] = [
            'name' => command_read("#$s Column Name:"),
            'datatype' => command_read("#$s Data type:", 0, ['varchar(45)', 'int(11)', 'datetime', 'date', 'time', 'bigint(20)']),
            'allow_null' => command_read_bool("#$s Allow Null"),
            'default' => command_read("#$s Default:", null),
        ];

        foreach ($entity_structs as $struct) {
            echo json_encode($struct)."\n";
        }
    }

    $entity_relationships = [];

    $r = 0;

    while (command_read_bool('Add relationship')) {

        $r += 1;

        $entity_relationships[] = [
            'type' => command_read("#$r Type:", 0, ['belongs_to', 'has_one', 'has_many']),
            'relate_to' => command_read("#$r Relate to:"),
            'relation_name' => command_read("#$r Relation name:"),
        ];

        foreach ($entity_relationships as $relationship) {
            echo json_encode($relationship)."\n";
        }
    }

    error_log(_generate_entity_file($entity_name, $entity_structs, $entity_relationships), 3, $file = ENTITY_DIR.'/'.$entity_name.'.php');
    echo $file."\n";
    error_log(_generate_dao_file($entity_name, $entity_structs, $entity_relationships), 3, $file = DAO_DIR.'/'.$entity_name.'.php');
    echo $file."\n";
    error_log(_generate_migration_file($entity_name, $entity_structs, $entity_relationships), 3, $file = migration_file_path($entity_name));
    echo $file."\n";

});/*}}}*/

command('entity:make-from-db', '从数据库表结构初始化 entity、dao、migration', function ()
{/*{{{*/
    $table_infos = db_query('show tables', [], unit_of_work_db_config_key());

    foreach ($table_infos as $table_info) {
        $entity_structs = $entity_relationships = [];
        $entity_name = $table = reset($table_info);

        if ($entity_name === MIGRATION_TABLE) {
            continue;
        }

        $schema_infos = db_query("show create table `$table`", [], unit_of_work_db_config_key());
        $schema_info = reset($schema_infos);

        $lines = explode("\n", $schema_info['Create Table']);

        foreach ($lines as $i => $line) {

            $line = trim($line);

            if (stristr($line, 'CONSTRAINT')) {
                unset($lines[$i]);
                continue;
            }

            if (stristr($line, 'CREATE TABLE')) continue;
            if (stristr($line, 'PRIMARY KEY')) continue;
            if (stristr($line, ') ENGINE=')) continue;
            if (stristr($line, '`id`')) continue;
            if (stristr($line, '`version`')) continue;
            if (stristr($line, '`create_time`')) continue;
            if (stristr($line, '`update_time`')) continue;
            if (stristr($line, '`delete_time`')) continue;


            preg_match('/^`(.*)`/', $line, $matches);
            if ($matches) {
                $entity_structs[] = [
                    'name' => $matches[1],
                ];
                continue;
            }

            preg_match('/^KEY `fk_'.$entity_name.'_(.*)_idx` \(`(.*)`\)/', $line, $matches);
            if ($matches) {
                $relate_to = preg_replace('/[0-9]/', '', $matches[1]);
                $relation_name = str_replace('_id', '', $matches[2]);
                $entity_relationships[] = [
                    'type' => 'belongs_to',
                    'relate_to' => $relate_to,
                    'relation_name' => $relation_name,
                ];
            }
        }

        $up_sql = str_replace(",\n)", "\n)", implode("\n", $lines));

        $migration = sprintf("# up\n%s;\n\n# down\ndrop table `%s`;", $up_sql, $entity_name);

        echo $entity_name.":\n";
        error_log(_generate_entity_file($entity_name, $entity_structs, $entity_relationships), 3, $file = ENTITY_DIR.'/'.$entity_name.'.php');
        echo $file."\n";
        error_log(_generate_dao_file($entity_name, $entity_structs, $entity_relationships), 3, $file = DAO_DIR.'/'.$entity_name.'.php');
        echo $file."\n";
        error_log($migration, 3, $file = migration_file_path($entity_name));
        echo $file."\n";
    }
});/*}}}*/
